﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Library.Linq;
using INDEX = System.UInt64;

namespace Library.Collections.Generic
{
    [DebuggerDisplay("Count={Count}")]
    public sealed class Vector<T> : IList<T>, IVector, IFirst<T>, IElementAt<T>, ILast<T>, IQuantifiable<T>, ISkip<T>, ITake<T>
    {
        public Vector(INDEX count)
        {
            data = new T[count];
        }
        private Vector(T[] data)
        {
            this.data = data;
        }
        T[] data;

        public T this[INDEX key]
        {
            get
            {
                return data[key];
            }
            set
            {
                data[key] = value;
            }
        }

        public bool Contains(T item, out INDEX index)
        {
            int n = Array.IndexOf(data, item);
            if (n < 0)
            {
                index = 0;
                return false;
            }
            index = (INDEX)n;
            return true;
        }

        public bool Contains(T item)
        {
            return Array.IndexOf(data, item) >= 0;
        }

        public INDEX Count
        {
            get
            {
                return (INDEX)data.Length;
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return ((IEnumerable<T>)data).GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return data.GetEnumerator();
        }

        public IEnumerator<T> GetEnumerator(Direction direction)
        {
            return new Library.Collections.Specialized.ArrayEnumeratorReversed<T>(data, 0, (ulong)data.Length);
        }

        System.Collections.IEnumerator Specialized.IEnumerableDirection.GetEnumerator(Direction direction)
        {
            return new Library.Collections.Specialized.ArrayEnumeratorReversed<T>(data, 0, (ulong)data.Length);
        }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        object IMap.this[object key]
        {
            get
            {
                return data[(int)key];
            }
            set
            {
                data[(int)key] = (T)value;
            }
        }

        object IMapReadOnly.this[object key]
        {
            get
            {
                return data[(int)key];
            }
        }

        object IMapWriteOnly.this[object key]
        {
            set
            {
                data[(int)key] = (T)value;
            }
        }

        bool IVectorReadOnly.Contains(object item, out object index)
        {
            int n = Array.IndexOf(data, item);
            if (n < 0)
            {
                index = 0;
                return false;
            }
            index = (INDEX)n;
            return true;
        }

        bool IContainer.Contains(object item)
        {
            return Array.IndexOf(data, item) >= 0;
        }

        bool IFirst<T>.First(out T item)
        {
            if (data.Length == 0)
            {
                item = default(T);
                return false;
            }
            item = data[0];
            return true;
        }

        bool IElementAt<T>.ElementAt(INDEX index, out T item)
        {
            if (index < (INDEX)data.Length)
            {
                item = data[index];
                return true;
            }
            item = default(T);
            return false;
        }

        bool ILast<T>.Last(out T item)
        {
            int n = data.Length;
            if (n == 0)
            {
                item = default(T);
                return false;
            }
            item = data[n - 1];
            return true;
        }

        bool IQuantifiable<T>.ForAll(Func<T, bool> predicate)
        {
            for (int i = 0, n = data.Length; i < n; i++)
                if (!predicate(data[i])) return false;
            return true;
        }

        bool IQuantifiable<T>.Exists()
        {
            return data.Length > 0;
        }

        bool IQuantifiable<T>.Exists(Func<T, bool> predicate)
        {
            for (int i = 0, n = data.Length; i < n; i++)
                if (predicate(data[i])) return true;
            return false;
        }

        IEnumerable<T> ISkip<T>.Skip(INDEX count)
        {
            return ReadOnly.AsReadOnly(data, count, (INDEX)this.data.Length - count);
        }

        IEnumerable<T> ITake<T>.Take(INDEX count)
        {
            return ReadOnly.AsReadOnly(data, 0, count);
        }

        public IVector<T> Clone(Direction direction, Access access)
        {
            if (direction == Direction.Forward)
            {
                if (access == Access.Read)
                {
                    T[] _data = new T[data.Length];
                    Array.Copy(data, _data, data.Length);
                    return new ReadOnly.Array<T>(_data);
                }
                else if (access == Access.ReadWrite)
                {
                    T[] _data = new T[data.Length];
                    Array.Copy(data, _data, data.Length);
                    return new Vector<T>(_data);
                }
                else throw new NotSupportedException();
            }
            else
            {
                if (access == Access.Read)
                {
                    T[] _data = new T[data.Length];
                    for (int i = 0, n = data.Length; i < n; ++i)
                        _data[i] = data[n - 1 - i];
                    return new ReadOnly.Array<T>(_data);
                }
                else if (access == Access.ReadWrite)
                {
                    T[] _data = new T[data.Length];
                    for (int i = 0, n = data.Length; i < n; ++i)
                        _data[i] = data[n - 1 - i];
                    return new Vector<T>(_data);
                }
                else throw new NotSupportedException();
            }
        }

        public IVector<T> Clone(Direction direction)
        {
            if (direction == Direction.Forward)
            {
                T[] _data = new T[data.Length];
                Array.Copy(data, _data, data.Length);
                return new Vector<T>(_data);
            }
            else
            {
                T[] _data = new T[data.Length];
                for (int i = 0, n = data.Length; i < n; ++i)
                    _data[i] = data[n - 1 - i];
                return new Vector<T>(_data);
            }
        }

        public IVector<T> Clone()
        {
            T[] _data = new T[data.Length];
            Array.Copy(data, _data, data.Length);
            return new Vector<T>(_data);
        }

        public IVector<T> Clone(Access access)
        {
            if (access == Access.Read)
            {
                T[] _data = new T[data.Length];
                Array.Copy(data, _data, data.Length);
                return new ReadOnly.Array<T>(_data);
            }
            else if (access == Access.ReadWrite)
            {
                T[] _data = new T[data.Length];
                Array.Copy(data, _data, data.Length);
                return new Vector<T>(_data);
            }
            else throw new NotSupportedException();
        }

        bool IList<T>.Add(T item, out INDEX index)
        {
            index = 0;
            return false;
        }

        bool IList<T>.Insert(INDEX index, T item)
        {
            return false;
        }

        bool IList<T>.RemoveAt(INDEX index)
        {
            return false;
        }

        bool ICollection<T>.Add(T item)
        {
            return false;
        }

        bool ICollection<T>.Remove(T item)
        {
            return false;
        }

        bool IClearable.Clear()
        {
            return false;
        }

        bool IIsFixedSize.IsFixedSize
        {
            get
            {
                return true;
            }
        }
    }
}
